package com.google.sky.flow;

import com.google.sky.Board;
import com.google.sky.domain.BoardImpl;
import com.google.sky.domain.Mark;
import com.google.sky.excepts.EndOfGameException;
import com.google.sky.register.GameStateRegister;
import com.google.sky.register.Settings;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;

import java.util.concurrent.Callable;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;

/**
 * @author: sk_y
 */
public class GameControllerImpl implements GameController {

    @Autowired
    private GameStateRegister gameStateRegister;

    private final AtomicBoolean isRunning = new AtomicBoolean(false);

    private BoardImpl board;

    private AtomicLong player1Time=new AtomicLong(0);
    private AtomicLong player2Time=new AtomicLong(0);
    private AtomicLong winner = new AtomicLong(-1);

    public BoardImpl getBoard() {
        return board;
    }

    public boolean isRunning(){
        return isRunning.get();
    }

    @Override
    public Board run(MoveController moveController1, MoveController moveController2, int h, int w) {
        if (!isRunning.get()) {
            board = new BoardImpl(w, h, gameStateRegister);
            Executors.newSingleThreadExecutor().submit(new Task(moveController1, moveController2, h, w, board));
            return board;
        }
        return null;
    }

    private class Task implements Callable<GameResult> {
        private MoveController moveController1;

        private MoveController moveController2;
        private BoardImpl board;
        private int h;
        private int w;

        private Task(MoveController moveController1, MoveController moveController2, int h, int w, BoardImpl board) {
            this.moveController1 = moveController1;
            this.moveController2 = moveController2;
            this.h = h;
            this.w = w;
            this.board = board;

        }

        @Override
        public GameResult call() throws Exception {
            isRunning.set(true);
            GameResult gameResult = new GameResult();

            int step = 0;
            gameStateRegister.setMoveOwner(Mark.X);
            while (!board.isFinished() && isRunning.get()) {
                step++;
                MoveController moveController = step % 2 == 1 ? moveController1 : moveController2;
                gameStateRegister.setMoveOwner(moveController.getMark());
                try{
                    moveController.nextMove(board);
                } catch (EndOfGameException e){
                    isRunning.set(false);
                    if (e.isOutOfBoard()){
                        long p1Time = player1Time.get();
                        long p2Time = player2Time.get();
                        winner.set(p1Time>p2Time ? 1L : (p1Time<p2Time ? 2L : 0L));
                    } else {
                        winner.set(step % 2 == 1? 2L : 1L);
                    }
                } catch (Exception e){
                    isRunning.set(false);
                    winner.set(step % 2 == 1? 2L : 1L);
                }
                if (step % 2==1){
                    player1Time.set(moveController.getAllMovesTime());
                } else {
                    player2Time.set(moveController.getAllMovesTime());
                }
                if (board.isFinished()){
                    winner.set(step % 2 == 1? 1L : 2L);
                }
                Thread.sleep(1000);
            }

            MoveController win = step % 2 == 1 ? moveController1 : moveController2;
            MoveController looser = winner.equals(moveController1) ? moveController2 : moveController1;
            gameResult.winner(win);
            gameResult.looser(looser);
            gameResult.setTotalMoves(step);
            isRunning.set(false);
            return gameResult;
        }
    }

    public Long getPlayer1Time() {
        return player1Time.get();
    }

    public Long getPlayer2Time() {
        return player2Time.get();
    }

    @Override
    public Long getWinner() {
        return winner.get();
    }
}
